﻿using System;
using System.Collections.Generic;
using System.Xml.Linq;

namespace WebChatApi.Common
{
    /// <summary>
    /// Class for dynamic object settings.
    /// </summary>
    public static class DynamicObjectSettings
    {
        #region Constants

        /// <summary>
        /// The XML node_ dynamic object settings
        /// </summary>
        private const string xmlNode_DynamicObjectSettings = "DynamicObjectSettings";

        /// <summary>
        /// The XML node_ item
        /// </summary>
        private const string xmlNode_Item = "Item";

        /// <summary>
        /// The XML attribute_ source name
        /// </summary>
        private const string xmlAttribute_SourceName = "SourceName";

        /// <summary>
        /// The XML attribute_ source name
        /// </summary>
        private const string xmlAttribute_Name = "Name";

        /// <summary>
        /// The XML attribute_ property name
        /// </summary>
        private const string xmlAttribute_PropertyName = "PropertyName";

        /// <summary>
        /// The XML attribute_ default value
        /// </summary>
        private const string xmlAttribute_DefaultValue = "DefaultValue";

        /// <summary>
        /// The XML attribute_ available
        /// </summary>
        private const string xmlAttribute_Available = "Available";

        /// <summary>
        /// The XML attribute_ property mappings
        /// </summary>
        private const string xmlNode_PropertyMappings = "PropertyMappings";

        #endregion

        #region Properties

        /// <summary>
        /// The initiialize locker
        /// </summary>
        private static object initiializeLocker = new object();

        /// <summary>
        /// The field collection
        /// </summary>
        private static Dictionary<string, bool> fieldCollection = new Dictionary<string, bool>();

        /// <summary>
        /// The mapping sets
        /// </summary>
        private static Dictionary<string, DynamicPropertyMappingCollection> mappingSets = new Dictionary<string, DynamicPropertyMappingCollection>();

        #endregion

        #region Initialize methods

        /// <summary>
        /// Initializes the field collection.
        /// </summary>
        /// <param name="configRoot">The config root.</param>
        private static void InitializeFieldCollection(XElement configRoot)
        {
            try
            {
                fieldCollection.Clear();

                if (configRoot != null)
                {
                    var fieldCollectionRoot = configRoot.Element("FieldSource");

                    if (fieldCollectionRoot != null)
                    {
                        // <Item Name="CommentOn" Available="True"/>
                        foreach (var item in fieldCollectionRoot.Elements(xmlNode_Item))
                        {
                            string name = item.GetAttributeValue(xmlAttribute_Name);
                            bool isAvailable = item.GetAttributeValue(xmlAttribute_Available).DBToBoolean();

                            if (!string.IsNullOrWhiteSpace(name))
                            {
                                fieldCollection.Merge(name, isAvailable);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new OperationFailureException("InitializeFieldCollection", ex, configRoot.ToString());
            }
        }

        /// <summary>
        /// Initializes the request.
        /// </summary>
        /// <param name="configRoot">The config root.</param>
        private static void InitializeMappings(XElement configRoot)
        {
            try
            {
                if (configRoot != null)
                {
                    var requestRoot = configRoot.Element(xmlNode_PropertyMappings);
                    InitializeMappingSets(mappingSets, requestRoot);
                }
            }
            catch (Exception ex)
            {
                throw new OperationFailureException("InitializeMappings", ex, configRoot.ToString());
            }
        }

        /// <summary>
        /// Initializes the mapping sets.
        /// </summary>
        /// <param name="collectionDictionary">The collection dictionary.</param>
        /// <param name="sectionRoot">The section root.</param>
        private static void InitializeMappingSets(Dictionary<string, DynamicPropertyMappingCollection> collectionDictionary, XElement sectionRoot)
        {
            if (collectionDictionary != null && sectionRoot != null)
            {
                collectionDictionary.Clear();

                foreach (var objectNameNode in sectionRoot.Elements())
                {
                    string objectName = objectNameNode.Name.LocalName;
                    if (!collectionDictionary.ContainsKey(objectName))
                    {
                        collectionDictionary.Add(objectName, new DynamicPropertyMappingCollection());
                    }

                    InitializeMappingCollection(collectionDictionary[objectName], objectNameNode);
                }
            }
        }

        /// <summary>
        /// Initializes the mapping collection.
        /// </summary>
        /// <param name="collction">The collction.</param>
        /// <param name="objectRoot">The object root.</param>
        private static void InitializeMappingCollection(DynamicPropertyMappingCollection collction, XElement objectRoot)
        {
            if (collction != null && objectRoot != null)
            {
                foreach (var one in objectRoot.Elements(xmlNode_Item))
                {
                    collction.AddPropertyMapping(ConvertDynamicPropertyMapping(one), true);
                }
            }
        }

        /// <summary>
        /// Converts the dynamic property mapping.
        /// </summary>
        /// <param name="objectRoot">The object root.</param>
        /// <returns></returns>
        private static DynamicPropertyMapping ConvertDynamicPropertyMapping(XElement objectRoot)
        {
            DynamicPropertyMapping mapping = null;

            if (objectRoot != null && objectRoot.Name.LocalName.Equals(xmlNode_Item))
            {
                var sourceName = objectRoot.GetAttributeValue(xmlAttribute_SourceName);

                if (!string.IsNullOrWhiteSpace(sourceName))
                {
                    mapping = new DynamicPropertyMapping();

                    mapping.SourceName = sourceName;
                    mapping.PropertyName = objectRoot.GetAttributeValue(xmlAttribute_PropertyName);
                    mapping.DefaultValue = objectRoot.GetAttributeValue(xmlAttribute_DefaultValue);

                    XElement subElement = objectRoot.FirstNode as XElement;

                    if (subElement != null)
                    {
                        mapping.ComplexObjectValue = new DynamicPropertyMappingCollection();
                        InitializeMappingCollection(mapping.ComplexObjectValue, objectRoot);
                    }
                }
            }

            return mapping;
        }

        /// <summary>
        /// Initializes the settings
        /// </summary>
        /// <param name="configurationRoot">The configuration root.</param>
        /// <exception cref="InitializationFailureException">InitializeMappingCollections</exception>
        public static void Initialize(XElement configurationRoot)
        {
            try
            {
                configurationRoot.CheckNullObject(configurationRoot, "configurationRoot");

                if (configurationRoot.Name.LocalName == xmlNode_DynamicObjectSettings)
                {
                    lock (initiializeLocker)
                    {
                        InitializeFieldCollection(configurationRoot);
                        InitializeMappings(configurationRoot);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new InitializationFailureException("Initialize", ex, configurationRoot.ToString());
            }
        }

        #endregion

        /// <summary>
        /// Gets the dynamic property mapping collection.
        /// </summary>
        /// <param name="objectName">Name of the object.</param>
        /// <returns></returns>
        public static DynamicPropertyMappingCollection GetDynamicPropertyMappingCollection(string objectName)
        {
            return (!string.IsNullOrWhiteSpace(objectName) && mappingSets.ContainsKey(objectName)) ? mappingSets[objectName] : null;
        }

        /// <summary>
        /// Determines whether the specified mapping is available.
        /// </summary>
        /// <param name="mapping">The mapping.</param>
        /// <returns>
        ///   <c>true</c> if the specified mapping is available; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsAvailable(DynamicPropertyMapping mapping)
        {
            return mapping != null && fieldCollection.ContainsKey(mapping.SourceName) && fieldCollection[mapping.SourceName];
        }
    }
}
